تجربههای وب روان و شبیه به اپلیکیشن را خلق کنید. این راهنمای جامع، شبهعناصر قدرتمند CSS View Transition را برای استایلدهی به ترنزیشنهای دینامیک صفحه، با مثالهای کاربردی و بهترین شیوهها بررسی میکند.
تسلط بر ترنزیشنهای نمای CSS: بررسی عمیق استایلدهی به شبهعناصر
در چشمانداز همواره در حال تحول توسعه وب، تلاش برای دستیابی به یک تجربه کاربری یکپارچه، روان و جذاب یک امر همیشگی است. سالهاست که توسعهدهندگان کوشیدهاند تا شکاف بین وب و اپلیکیشنهای نیتیو را، بهویژه در زمینه روانی ترنزیشنهای صفحه، پر کنند. ناوبری سنتی وب اغلب منجر به یک بارگذاری مجدد زننده و تمامصفحه میشود—یک صفحه سفید خالی که غرق شدن کاربر در محتوا را برای لحظهای میشکند. اپلیکیشنهای تکصفحهای (SPAs) این مشکل را کاهش دادهاند، اما ایجاد ترنزیشنهای سفارشی و معنادار همچنان یک کار پیچیده و اغلب شکننده باقی مانده است که بهشدت به کتابخانههای جاوا اسکریپت و مدیریت وضعیت پیچیده متکی است.
با CSS View Transitions API آشنا شوید، یک فناوری تحولآفرین که آماده است تا نحوه مدیریت تغییرات UI در وب را متحول کند. این API قدرتمند یک مکانیزم ساده اما فوقالعاده انعطافپذیر برای انیمیشن بین حالتهای مختلف DOM فراهم میکند و ساخت تجربههای صیقلی و شبیه به اپلیکیشن که کاربران انتظار دارند را آسانتر از همیشه میسازد. در قلب قدرت این API، مجموعهای از شبهعناصر (pseudo-elements) جدید CSS قرار دارد. اینها انتخابگرهای معمولی شما نیستند؛ آنها عناصر دینامیک و موقتی هستند که توسط مرورگر تولید میشوند تا به شما کنترل دقیقی بر هر مرحله از یک ترنزیشن بدهند. این راهنما شما را به یک بررسی عمیق از این درخت شبهعناصر میبرد و چگونگی استایلدهی به هر جزء برای ساخت انیمیشنهای خیرهکننده، با کارایی بالا و قابل دسترس برای مخاطبان جهانی را بررسی میکند.
آناتومی یک ترنزیشن نما
قبل از اینکه بتوانیم یک ترنزیشن را استایلدهی کنیم، باید بفهمیم که وقتی یکی از آنها فعال میشود، در پشت صحنه چه اتفاقی میافتد. هنگامی که شما یک ترنزیشن نما را آغاز میکنید (برای مثال، با فراخوانی document.startViewTransition())، مرورگر یک سری مراحل را انجام میدهد:
- ثبت وضعیت قدیمی: مرورگر یک «اسکرینشات» از وضعیت فعلی صفحه میگیرد.
- بهروزرسانی DOM: کد شما سپس تغییرات خود را در DOM اعمال میکند (مثلاً، ناوبری به یک نمای جدید، افزودن یا حذف عناصر).
- ثبت وضعیت جدید: پس از تکمیل بهروزرسانی DOM، مرورگر یک اسکرینشات از وضعیت جدید میگیرد.
- ساخت درخت شبهعناصر: سپس مرورگر یک درخت موقت از شبهعناصر را در لایه رویی (overlay) صفحه میسازد. این درخت حاوی تصاویر ثبتشده از وضعیتهای قدیمی و جدید است.
- انیمیشن: انیمیشنهای CSS به این شبهعناصر اعمال میشوند و یک ترنزیشن روان از وضعیت قدیمی به وضعیت جدید ایجاد میکنند. حالت پیشفرض یک cross-fade ساده است.
- پاکسازی: پس از اتمام انیمیشنها، درخت شبهعناصر حذف میشود و کاربر میتواند با DOM جدید و زنده تعامل کند.
کلید سفارشیسازی، این درخت موقت شبهعناصر است. آن را مانند مجموعهای از لایهها در یک ابزار طراحی در نظر بگیرید که بهطور موقت روی صفحه شما قرار گرفتهاند. شما کنترل کامل CSS بر روی این لایهها دارید. ساختاری که با آن کار خواهید کرد به این صورت است:
- ::view-transition
- ::view-transition-group(*)
- ::view-transition-image-pair(*)
- ::view-transition-old(*)
- ::view-transition-new(*)
- ::view-transition-image-pair(*)
- ::view-transition-group(*)
بیایید بررسی کنیم که هر یک از این شبهعناصر چه چیزی را نشان میدهند.
معرفی بازیگران شبهعنصر
::view-transition: این ریشه کل ساختار است. این یک عنصر واحد است که تمام ویوپورت را پر میکند و روی تمام محتوای دیگر صفحه قرار میگیرد. این عنصر به عنوان کانتینر برای تمام گروههای در حال ترنزیشن عمل میکند و مکانی عالی برای تنظیم ویژگیهای کلی ترنزیشن مانند مدت زمان یا تابع زمانبندی (timing function) است.
::view-transition-group(*): برای هر عنصر متمایز در حال ترنزیشن (که با ویژگی CSS view-transition-name شناسایی میشود)، یک گروه ایجاد میشود. این شبهعنصر مسئول انیمیشن موقعیت و اندازه محتوای خود است. اگر کارتی دارید که از یک طرف صفحه به طرف دیگر حرکت میکند، این ::view-transition-group است که در واقع در حال حرکت است.
::view-transition-image-pair(*): این عنصر که درون گروه قرار گرفته است، به عنوان یک کانتینر و یک ماسک برش (clipping mask) برای نماهای قدیمی و جدید عمل میکند. نقش اصلی آن حفظ افکتهایی مانند border-radius یا transform در طول انیمیشن و مدیریت انیمیشن پیشفرض cross-fade است.
::view-transition-old(*): این نمایانگر «اسکرینشات» یا نمای رندر شده از عنصر در وضعیت قدیمی آن (قبل از تغییر DOM) است. به طور پیشفرض، از opacity: 1 به opacity: 0 انیمیشن میشود.
::view-transition-new(*): این نمایانگر «اسکرینشات» یا نمای رندر شده از عنصر در وضعیت جدید آن (بعد از تغییر DOM) است. به طور پیشفرض، از opacity: 0 به opacity: 1 انیمیشن میشود.
ریشه: استایلدهی به شبهعنصر ::view-transition
شبهعنصر ::view-transition بوم نقاشی است که کل انیمیشن شما روی آن کشیده میشود. به عنوان کانتینر سطح بالا، مکانی ایدهآل برای تعریف ویژگیهایی است که باید به طور کلی برای ترنزیشن اعمال شوند. به طور پیشفرض، مرورگر مجموعهای از انیمیشنها را فراهم میکند، اما شما به راحتی میتوانید آنها را بازنویسی کنید.
به عنوان مثال، ترنزیشن پیشفرض یک cross-fade است که ۲۵۰ میلیثانیه طول میکشد. اگر میخواهید این را برای هر ترنزیشن در سایت خود تغییر دهید، میتوانید شبهعنصر ریشه را هدف قرار دهید:
::view-transition {
animation-duration: 500ms;
animation-timing-function: ease-in-out;
}
این قانون ساده اکنون باعث میشود که تمام محو شدنهای پیشفرض صفحه دو برابر طول بکشند و از یک منحنی 'ease-in-out' استفاده کنند، که به آنها حس کمی متفاوتی میدهد. در حالی که میتوانید انیمیشنهای پیچیدهای را در اینجا اعمال کنید، به طور کلی بهتر است از آن برای تعریف زمانبندی و easing جهانی استفاده شود و اجازه دهید شبهعناصر خاصتر، طراحی دقیق جزئیات را بر عهده بگیرند.
گروهبندی و نامگذاری: قدرت `view-transition-name`
به صورت پیشفرض و بدون هیچ کار اضافهای، View Transition API یک cross-fade برای کل صفحه فراهم میکند. این کار توسط یک گروه شبهعنصر برای ریشه انجام میشود. قدرت واقعی API زمانی آشکار میشود که شما میخواهید عناصر خاص و منفرد را بین حالتها ترنزیشن دهید. به عنوان مثال، کاری کنید که یک تصویر بندانگشتی محصول در صفحه لیست، به طور یکپارچه بزرگ شده و به موقعیت تصویر اصلی محصول در صفحه جزئیات منتقل شود.
برای اینکه به مرورگر بگویید دو عنصر در حالتهای مختلف DOM از نظر مفهومی یکسان هستند، از ویژگی CSS view-transition-name استفاده میکنید. این ویژگی باید هم به عنصر شروع و هم به عنصر پایان اعمال شود.
/* در CSS صفحه لیست */
.product-thumbnail {
view-transition-name: product-image;
}
/* در CSS صفحه جزئیات */
.main-product-image {
view-transition-name: product-image;
}
با دادن یک نام منحصربهفرد به هر دو عنصر ('product-image' در این مورد)، شما به مرورگر دستور میدهید: «به جای اینکه فقط صفحه قدیمی را محو کنی و صفحه جدید را ظاهر کنی، یک ترنزیشن ویژه برای این عنصر خاص ایجاد کن.» مرورگر اکنون یک ::view-transition-group(product-image) اختصاصی برای مدیریت انیمیشن آن به طور جداگانه از محو شدن ریشه ایجاد میکند. این مفهوم اساسی است که افکت ترنزیشن محبوب «مورفینگ» یا «عنصر مشترک» را امکانپذیر میسازد.
نکته مهم: در هر لحظه از یک ترنزیشن، یک view-transition-name باید منحصربهفرد باشد. شما نمیتوانید دو عنصر قابل مشاهده با نام یکسان در یک زمان داشته باشید.
استایلدهی عمیق: شبهعناصر اصلی
با نامگذاری عناصر، اکنون میتوانیم به استایلدهی شبهعناصر خاصی که مرورگر برای آنها تولید میکند، بپردازیم. اینجاست که میتوانید انیمیشنهای واقعاً سفارشی و выразительный بسازید.
`::view-transition-group(name)`: حرکتدهنده
تنها مسئولیت گروه، ترنزیشن از اندازه و موقعیت عنصر قدیمی به اندازه و موقعیت عنصر جدید است. این گروه ظاهر واقعی محتوا را شامل نمیشود، بلکه فقط کادر مرزی آن را در بر میگیرد. آن را به عنوان یک قاب متحرک در نظر بگیرید.
به طور پیشفرض، مرورگر ویژگیهای transform و width/height آن را انیمیشن میکند. شما میتوانید این را برای ایجاد افکتهای مختلف بازنویسی کنید. به عنوان مثال، میتوانید با انیمیشن دادن آن در یک مسیر منحنی، یک قوس به حرکت آن اضافه کنید، یا کاری کنید که در طول مسیر خود بزرگ و کوچک شود.
::view-transition-group(product-image) {
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
در این مثال، ما یک تابع easing خاص را فقط به حرکت تصویر محصول اعمال میکنیم، که باعث میشود حس پویاتر و فیزیکیتری داشته باشد، بدون اینکه بر محو شدن پیشفرض بقیه صفحه تأثیر بگذارد.
`::view-transition-image-pair(name)`: برشدهنده و محوکننده
جفت تصویر که درون گروه متحرک قرار دارد، نماهای قدیمی و جدید را نگه میدارد. این به عنوان یک ماسک برش عمل میکند، بنابراین اگر عنصر شما دارای border-radius باشد، جفت تصویر تضمین میکند که محتوا در طول انیمیشن اندازه و موقعیت با آن شعاع بریده شده باقی بماند. وظیفه اصلی دیگر آن، هماهنگ کردن cross-fade پیشفرض بین محتوای قدیمی و جدید است.
شما ممکن است بخواهید این عنصر را برای اطمینان از ثبات بصری یا برای ایجاد افکتهای پیشرفتهتر استایلدهی کنید. یک ویژگی کلیدی که باید در نظر گرفته شود isolation: isolate است. این امر در صورتی که قصد استفاده از افکتهای پیشرفته mix-blend-mode روی فرزندان (قدیمی و جدید) را داشته باشید، حیاتی است، زیرا یک زمینه پشتهسازی (stacking context) جدید ایجاد میکند و از تأثیر ترکیب رنگ بر عناصر خارج از گروه ترنزیشن جلوگیری میکند.
::view-transition-image-pair(product-image) {
isolation: isolate;
}
`::view-transition-old(name)` و `::view-transition-new(name)`: ستارگان نمایش
اینها شبهعناصری هستند که ظاهر بصری عنصر شما را قبل و بعد از تغییر DOM نشان میدهند. اینجاست که بیشتر کارهای انیمیشن سفارشی شما انجام خواهد شد. به طور پیشفرض، مرورگر یک انیمیشن cross-fade ساده را با استفاده از opacity و mix-blend-mode روی آنها اجرا میکند. برای ایجاد یک انیمیشن سفارشی، ابتدا باید انیمیشن پیشفرض را خاموش کنید.
::view-transition-old(name),
::view-transition-new(name) {
animation: none;
}
پس از غیرفعال کردن انیمیشن پیشفرض، شما آزاد هستید که انیمیشن خود را اعمال کنید. بیایید چند الگوی رایج را بررسی کنیم.
انیمیشن سفارشی: اسلاید
به جای cross-fade، بیایید محتوای یک کانتینر را به صورت اسلاید وارد کنیم. به عنوان مثال، هنگام ناوبری بین مقالات، میخواهیم متن مقاله جدید از سمت راست وارد شود در حالی که متن قدیمی به سمت چپ خارج میشود.
ابتدا، keyframeها را تعریف کنید:
@keyframes slide-from-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes slide-to-left {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
اکنون، این انیمیشنها را به شبهعناصر قدیمی و جدید برای عنصر نامگذاری شده 'article-content' اعمال کنید.
::view-transition-old(article-content) {
animation: 300ms ease-out forwards slide-to-left;
}
::view-transition-new(article-content) {
animation: 300ms ease-out forwards slide-from-right;
}
انیمیشن سفارشی: چرخش سهبعدی
برای یک افکت چشمگیرتر، میتوانید یک چرخش کارت سهبعدی ایجاد کنید. این کار نیاز به انیمیشن دادن ویژگی transform با rotateY و همچنین مدیریت backface-visibility دارد.
/* گروه به یک زمینه سهبعدی نیاز دارد */
::view-transition-group(card-flipper) {
transform-style: preserve-3d;
}
/* جفت تصویر نیز باید زمینه سهبعدی را حفظ کند */
::view-transition-image-pair(card-flipper) {
transform-style: preserve-3d;
}
/* نمای قدیمی از ۰ تا ۱۸۰- درجه میچرخد */
::view-transition-old(card-flipper) {
animation: 600ms ease-in forwards flip-out;
backface-visibility: hidden;
}
/* نمای جدید از ۱۸۰ تا ۰ درجه میچرخد */
::view-transition-new(card-flipper) {
animation: 600ms ease-out forwards flip-in;
backface-visibility: hidden;
}
@keyframes flip-out {
from { transform: rotateY(0deg); }
to { transform: rotateY(-180deg); }
}
@keyframes flip-in {
from { transform: rotateY(180deg); }
to { transform: rotateY(0deg); }
}
مثالهای کاربردی و تکنیکهای پیشرفته
تئوری مفید است، اما کاربرد عملی جایی است که ما واقعاً یاد میگیریم. بیایید چند سناریوی رایج و نحوه حل آنها با شبهعناصر ترنزیشن نما را بررسی کنیم.
مثال: تصویر بندانگشتی کارت «مورفینگ»
این ترنزیشن کلاسیک عنصر مشترک است. یک گالری از پروفایلهای کاربران را تصور کنید. هر پروفایل یک کارت با یک آواتار است. وقتی روی یک کارت کلیک میکنید، به صفحه جزئیات هدایت میشوید که در آن همان آواتار به طور برجسته در بالا نمایش داده میشود.
مرحله ۱: تخصیص نام
در صفحه گالری، تصویر آواتار یک نام میگیرد. نام باید برای هر کارت منحصربهفرد باشد، به عنوان مثال، بر اساس شناسه کاربر.
/* در gallery-item.css */
.card-avatar { view-transition-name: avatar-user-123; }
در صفحه جزئیات پروفایل، آواتار بزرگ هدر دقیقاً همان نام را میگیرد.
/* در profile-page.css */
.profile-header-avatar { view-transition-name: avatar-user-123; }
مرحله ۲: سفارشیسازی انیمیشن
به طور پیشفرض، مرورگر آواتار را حرکت و مقیاس میدهد، اما محتوا را نیز cross-fade میکند. اگر تصویر یکسان باشد، این محو شدن غیرضروری است و میتواند باعث یک سوسو زدن جزئی شود. ما میتوانیم آن را غیرفعال کنیم.
/* ستاره (*) در اینجا یک wildcard برای هر گروه نامگذاری شده است */
::view-transition-image-pair(*) {
/* غیرفعال کردن محو شدن پیشفرض */
animation-duration: 0s;
}
صبر کنید، اگر محو شدن را غیرفعال کنیم، محتوا چگونه تغییر میکند؟ برای عناصر مشترک که نماهای قدیمی و جدید یکسان هستند، مرورگر به اندازه کافی هوشمند است که فقط از یک نما برای کل ترنزیشن استفاده کند. `image-pair` در اصل فقط یک تصویر را نگه میدارد، بنابراین غیرفعال کردن محو شدن به سادگی این بهینهسازی را آشکار میکند. برای عناصری که محتوای آنها واقعاً تغییر میکند، شما به یک انیمیشن سفارشی به جای محو شدن پیشفرض نیاز دارید.
مدیریت تغییرات نسبت ابعاد (Aspect Ratio)
یک چالش رایج زمانی به وجود میآید که یک عنصر در حال ترنزیشن نسبت ابعاد خود را تغییر میدهد. به عنوان مثال، یک تصویر بندانگشتی افقی ۱۶:۹ در صفحه لیست ممکن است به یک آواتار مربعی ۱:۱ در صفحه جزئیات ترنزیشن پیدا کند. رفتار پیشفرض مرورگر این است که عرض و ارتفاع را به طور مستقل انیمیشن میکند، که منجر به فشرده یا کشیده به نظر رسیدن تصویر در طول ترنزیشن میشود.
راه حل زیباست. ما به ::view-transition-group اجازه میدهیم تغییر اندازه و موقعیت را مدیریت کند، اما استایلدهی تصاویر قدیمی و جدید را درون آن بازنویسی میکنیم.
هدف این است که «اسکرینشاتهای» قدیمی و جدید کانتینر خود را بدون تغییر شکل پر کنند. ما میتوانیم این کار را با تنظیم عرض و ارتفاع آنها به ۱۰۰٪ و اجازه دادن به ویژگی پیشفرض object-fit مرورگر (که از عنصر اصلی به ارث برده میشود) برای مدیریت صحیح مقیاسبندی انجام دهیم.
::view-transition-old(hero-image),
::view-transition-new(hero-image) {
/* جلوگیری از تغییر شکل با پر کردن کانتینر */
width: 100%;
height: 100%;
/* بازنویسی cross-fade پیشفرض برای دیدن واضح افکت */
animation: none;
}
با این CSS، `image-pair` نسبت ابعاد خود را به آرامی انیمیشن میکند و تصاویر داخل آن به درستی برش داده یا letterbox میشوند (بسته به مقدار `object-fit` آنها)، درست همانطور که در یک کانتینر معمولی بودند. سپس میتوانید انیمیشنهای سفارشی خود، مانند یک cross-fade، را بر روی این هندسه اصلاح شده اضافه کنید.
دیباگینگ و پشتیبانی مرورگرها
استایلدهی به عناصری که فقط برای کسری از ثانیه وجود دارند میتواند دشوار باشد. خوشبختانه، مرورگرهای مدرن ابزارهای توسعهدهنده عالی برای این کار فراهم میکنند. در Chrome یا Edge DevTools، میتوانید به پنل «Animations» بروید، و هنگامی که یک ترنزیشن نما را فعال میکنید، میتوانید آن را متوقف کنید. با متوقف شدن انیمیشن، میتوانید از پنل «Elements» برای بازرسی کل درخت شبهعنصر `::view-transition` درست مانند هر بخش دیگری از DOM استفاده کنید. شما میتوانید استایلهای اعمال شده را ببینید و حتی آنها را در زمان واقعی تغییر دهید تا انیمیشنهای خود را کامل کنید.
از اواخر سال ۲۰۲۳، View Transitions API در مرورگرهای مبتنی بر Chromium (Chrome، Edge، Opera) پشتیبانی میشود. پیادهسازیها در Firefox و Safari در حال انجام است. این امر آن را به یک کاندیدای عالی برای بهبود تدریجی (progressive enhancement) تبدیل میکند. کاربرانی که مرورگرهای پشتیبانی شده دارند، یک تجربه لذتبخش و بهبود یافته دریافت میکنند، در حالی که کاربران سایر مرورگرها ناوبری استاندارد و فوری را دریافت میکنند. شما میتوانید پشتیبانی را در CSS بررسی کنید:
@supports (view-transition: none) {
/* تمام استایلهای view-transition اینجا قرار میگیرند */
::view-transition-old(my-element) { ... }
}
بهترین شیوهها برای مخاطبان جهانی
هنگام پیادهسازی انیمیشنها، توجه به طیف متنوعی از کاربران و دستگاهها در سراسر جهان حیاتی است.
عملکرد: انیمیشنها باید سریع و روان باشند. به انیمیشن دادن ویژگیهای CSS که پردازش آنها برای مرورگر ارزان است، عمدتاً transform و opacity، پایبند باشید. انیمیشن دادن ویژگیهایی مانند width، height یا margin میتواند باعث محاسبه مجدد layout در هر فریم شود، که منجر به لکنت و تجربه ضعیف، به ویژه در دستگاههای با قدرت کمتر میشود.
دسترسپذیری: برخی از کاربران از انیمیشنها دچار بیماری حرکت یا ناراحتی میشوند. تمام سیستمعاملهای اصلی یک اولویت کاربری برای کاهش حرکت فراهم میکنند. ما باید به این احترام بگذاریم. مدیا کوئری prefers-reduced-motion به شما امکان میدهد انیمیشنهای خود را برای این کاربران غیرفعال یا ساده کنید.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
/* از تمام انیمیشنهای سفارشی صرفنظر کنید و از یک محو شدن سریع و ساده استفاده کنید */
animation: none !important;
}
}
تجربه کاربری (UX): ترنزیشنهای خوب هدفمند هستند. آنها باید توجه کاربر را هدایت کنند و در مورد تغییری که در UI رخ میدهد، زمینه فراهم کنند. انیمیشنی که بیش از حد کند باشد میتواند باعث شود یک اپلیکیشن کند به نظر برسد، در حالی که انیمیشنی که بیش از حد پر زرق و برق باشد میتواند حواسپرتکن باشد. مدت زمان ترنزیشن را بین ۲۰۰ تا ۵۰۰ میلیثانیه هدفگذاری کنید. هدف این است که انیمیشن بیشتر حس شود تا اینکه دیده شود.
نتیجهگیری: آینده روان است
CSS View Transitions API، و به طور خاص درخت شبهعناصر قدرتمند آن، نشاندهنده یک جهش بزرگ برای رابطهای کاربری وب است. این API به توسعهدهندگان یک مجموعه ابزار نیتیو، با کارایی بالا و بسیار قابل سفارشیسازی برای ایجاد ترنزیشنهای روان و حالتمندی میدهد که زمانی حوزه انحصاری اپلیکیشنهای نیتیو بود. با درک نقشهای ::view-transition، ::view-transition-group و جفتهای تصویر old/new، میتوانید فراتر از محو شدنهای ساده بروید و انیمیشنهای پیچیده و معناداری را طراحی کنید که قابلیت استفاده را افزایش داده و کاربران را به وجد میآورد.
با گسترش پشتیبانی مرورگرها، این API به بخش جداییناپذیر جعبه ابزار توسعهدهنده فرانتاند مدرن تبدیل خواهد شد. با پذیرش قابلیتهای آن و پایبندی به بهترین شیوهها برای عملکرد و دسترسپذیری، میتوانیم وبی بسازیم که نه تنها کاربردیتر، بلکه زیباتر و شهودیتر برای همه، در همه جا باشد.